// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "VulkanMain.hpp"
#include "glm/detail/type_mat.hpp"
#include "glm/fwd.hpp"
#include "glm/gtx/simd_quat.hpp"
#include "glm/ext.hpp"
#include "cube_data.h"

#include <android/log.h>



// Android log function wrappers
static const char *kTAG = "Vulkan-Cube";
#define LOGI(...) \
  ((void)__android_log_print(ANDROID_LOG_INFO, kTAG, __VA_ARGS__))
#define LOGW(...) \
  ((void)__android_log_print(ANDROID_LOG_WARN, kTAG, __VA_ARGS__))
#define LOGE(...) \
  ((void)__android_log_print(ANDROID_LOG_ERROR, kTAG, __VA_ARGS__))

// Vulkan call wrapper
#define CALL_VK(func)                                                 \
  if (VK_SUCCESS != (func)) {                                         \
    __android_log_print(ANDROID_LOG_ERROR, "Tutorial ",               \
                        "Vulkan error. File[%s], line[%d]", __FILE__, \
                        __LINE__);                                    \
    assert(false);                                                    \
  }


VulkanInfo vkInfo;
VkPipelineShaderStageCreateInfo shaderStages[2];
// Android Native App pointer...
android_app *androidAppCtx = nullptr;
float MVPmatrix[16] = {0};

// A helper function
bool MapMemoryTypeToIndex(uint32_t typeBits, VkFlags requirements_mask,
                          uint32_t *typeIndex) {
    VkPhysicalDeviceMemoryProperties memoryProperties;
    vkGetPhysicalDeviceMemoryProperties(vkInfo.device.gpuDevice_, &memoryProperties);
    // Search memtypes to find first index with those properties
    for (uint32_t i = 0; i < 32; i++) {
        if ((typeBits & 1) == 1) {
            // Type is available, does it match user properties?
            if ((memoryProperties.memoryTypes[i].propertyFlags & requirements_mask) ==
                requirements_mask) {
                *typeIndex = i;
                return true;
            }
        }
        typeBits >>= 1;
    }
    return false;
}

void CreateVulkanInstance(ANativeWindow *) {
    // Create the Vulkan instance

    VkApplicationInfo appInfo = {
            .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
            .pNext = nullptr,
            .apiVersion = VK_MAKE_VERSION(1, 0, 0),
            .applicationVersion = VK_MAKE_VERSION(1, 0, 0),
            .engineVersion = VK_MAKE_VERSION(1, 0, 0),
            .pApplicationName = "tutorial_cube",
            .pEngineName = "jjj",
    };

    std::vector<const char *> extensions;
    extensions.push_back("VK_KHR_surface");
    extensions.push_back("VK_KHR_android_surface");

    VkInstanceCreateInfo instanceCreateInfo{
            .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
            .pNext = nullptr,
            .pApplicationInfo = &appInfo,
            .enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
            .ppEnabledExtensionNames = extensions.data(),
            .enabledLayerCount = 0,
            .ppEnabledLayerNames = nullptr,
    };
    CALL_VK(vkCreateInstance(&instanceCreateInfo, nullptr, &vkInfo.instance_));

}

// Create vulkan device
void CreateVulkanDevice() {
    std::vector<const char *> device_extensions;
    device_extensions.push_back("VK_KHR_swapchain");
    // Find one GPU to use:
    // On Android, every GPU device is equal -- supporting
    // graphics/compute/present
    // for this sample, we use the very first GPU device found on the system
    uint32_t gpuCount = 0;
    CALL_VK(vkEnumeratePhysicalDevices(vkInfo.instance_, &gpuCount, nullptr));
    VkPhysicalDevice tmpGpus[gpuCount];
    CALL_VK(vkEnumeratePhysicalDevices(vkInfo.instance_, &gpuCount, tmpGpus));
    vkInfo.device.gpuDevice_ = tmpGpus[0];  // Pick up the first GPU Device

    // Find a GFX queue family
    uint32_t queueFamilyCount;
    vkGetPhysicalDeviceQueueFamilyProperties(vkInfo.device.gpuDevice_, &queueFamilyCount, nullptr);
    assert(queueFamilyCount);
    std::vector<VkQueueFamilyProperties> queueFamilyProperties(queueFamilyCount);
    vkGetPhysicalDeviceQueueFamilyProperties(vkInfo.device.gpuDevice_, &queueFamilyCount,
                                             queueFamilyProperties.data());

    uint32_t index;
    for (index = 0; index < queueFamilyCount; index++) {
        if (queueFamilyProperties[index].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
            break;
        }
    }
    assert(index < queueFamilyCount);
    vkInfo.device.queueFamilyIndex_ = index;

    // Create a logical device (vulkan device)
    float priorities[] = {
            1.0f,
    };
    VkDeviceQueueCreateInfo queueCreateInfo{
            .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
            .queueCount = 1,
            .queueFamilyIndex = index,
            .pQueuePriorities = priorities,
    };

    VkDeviceCreateInfo deviceCreateInfo{
            .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
            .pNext = nullptr,
            .queueCreateInfoCount = 1,
            .pQueueCreateInfos = &queueCreateInfo,
            .enabledLayerCount = 0,
            .ppEnabledLayerNames = nullptr,
            .enabledExtensionCount = static_cast<uint32_t>(device_extensions.size()),
            .ppEnabledExtensionNames = device_extensions.data(),
            .pEnabledFeatures = nullptr,
    };

    CALL_VK(vkCreateDevice(vkInfo.device.gpuDevice_, &deviceCreateInfo, nullptr, &vkInfo.device.logicalDevice_));
    vkGetDeviceQueue(vkInfo.device.logicalDevice_, vkInfo.device.queueFamilyIndex_, 0, &vkInfo.device.queue_);
}

void CreateCommandBuffer() {
    // Create a pool of command buffers to allocate command buffer from
    VkCommandPoolCreateInfo cmdPoolCreateInfo{
            .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
            .pNext = nullptr,
            .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
            .queueFamilyIndex = vkInfo.device.queueFamilyIndex_,
    };
    CALL_VK(vkCreateCommandPool(vkInfo.device.logicalDevice_, &cmdPoolCreateInfo, nullptr,
                                &vkInfo.command.pool_));

    // Record a command buffer that just clear the screen
    // 1 command buffer draw in 1 framebuffer
    // In our case we need 2 command as we have 2 framebuffer
    vkInfo.command.length_ = vkInfo.swapchain.length_;
    vkInfo.command.buffer_ = new VkCommandBuffer[vkInfo.command.length_];
    VkCommandBufferAllocateInfo cmdBufferCreateInfo{
            .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
            .pNext = nullptr,
            .commandPool = vkInfo.command.pool_,
            .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
            .commandBufferCount = vkInfo.command.length_,
    };
    CALL_VK(vkAllocateCommandBuffers(vkInfo.device.logicalDevice_, &cmdBufferCreateInfo,
                                     vkInfo.command.buffer_));
}

void CreateSwapChain(ANativeWindow *window) {

    memset(&vkInfo.swapchain, 0, sizeof(vkInfo.swapchain));

    VkAndroidSurfaceCreateInfoKHR createInfo{
            .sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR,
            .pNext = nullptr,
            .flags = 0,
            .window = window
    };

    VkSurfaceKHR androidSurface;
    CALL_VK(vkCreateAndroidSurfaceKHR(vkInfo.instance_, &createInfo, nullptr,
                                      &androidSurface));

    // Query the list of supported surface format and choose one we like
    uint32_t formatCount = 0;
    vkGetPhysicalDeviceSurfaceFormatsKHR(vkInfo.device.gpuDevice_, androidSurface,
                                         &formatCount, nullptr);
    VkSurfaceFormatKHR *formats = new VkSurfaceFormatKHR[formatCount];
    vkGetPhysicalDeviceSurfaceFormatsKHR(vkInfo.device.gpuDevice_, androidSurface,
                                         &formatCount, formats);
    LOGI("Got %d formats", formatCount);

    uint32_t chosenFormat;
    for (chosenFormat = 0; chosenFormat < formatCount; chosenFormat++) {
        if (formats[chosenFormat].format == VK_FORMAT_R8G8B8A8_UNORM)
            break;
    }
    assert(chosenFormat < formatCount);

    // Get the surface capabilities because:
    //   - It contains the minimal and max length of the chain, we will need it
    //   - It's necessary to query the supported surface format (R8G8B8A8 for
    //   instance ...)
    VkSurfaceCapabilitiesKHR surfaceCapabilities;
    vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vkInfo.device.gpuDevice_, androidSurface,
                                              &surfaceCapabilities);
    vkInfo.swapchain.displaySize_ = surfaceCapabilities.currentExtent;
    vkInfo.swapchain.displayFormat_ = formats[chosenFormat].format;

    LOGI("vkInfo.swapchain.displaySize %d x %d",vkInfo.swapchain.displaySize_.width,vkInfo.swapchain.displaySize_.height);

    assert(surfaceCapabilities.supportedCompositeAlpha | VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR);

    // **********************************************************
    // Create a swap chain (here we choose the minimum available number of surface
    // in the chain)
    VkSwapchainCreateInfoKHR swapchainCreateInfo{
            .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
            .pNext = nullptr,
            .surface = androidSurface,
            .minImageCount = surfaceCapabilities.minImageCount,
            .imageFormat = formats[chosenFormat].format,
            .imageColorSpace = formats[chosenFormat].colorSpace,
            .imageExtent = surfaceCapabilities.currentExtent,
            .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
            .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
            .imageArrayLayers = 1,
            .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
            .queueFamilyIndexCount = 1,
            .pQueueFamilyIndices = &vkInfo.device.queueFamilyIndex_,
            .compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
            .presentMode = VK_PRESENT_MODE_FIFO_KHR,
            .oldSwapchain = VK_NULL_HANDLE,
            .clipped = VK_FALSE,
    };
    CALL_VK(vkCreateSwapchainKHR(vkInfo.device.logicalDevice_, &swapchainCreateInfo, nullptr,
                                 &vkInfo.swapchain.swapchain_));

    // Get the length of the created swap chain
    // query display attachment to swapchain
    CALL_VK(vkGetSwapchainImagesKHR(vkInfo.device.logicalDevice_, vkInfo.swapchain.swapchain_,
                                    &vkInfo.swapchain.length_, nullptr));

    LOGI("swapchain length %d", vkInfo.swapchain.length_);
    vkInfo.swapchain.displayImages_.resize(vkInfo.swapchain.length_);
    CALL_VK(vkGetSwapchainImagesKHR(vkInfo.device.logicalDevice_, vkInfo.swapchain.swapchain_,
                                    &vkInfo.swapchain.length_,
                                    vkInfo.swapchain.displayImages_.data()));

    // create image view for each swapchain image
    vkInfo.swapchain.displayViews_.resize(vkInfo.swapchain.length_);
    for (uint32_t i = 0; i < vkInfo.swapchain.length_; i++) {
        VkImageViewCreateInfo viewCreateInfo = {
                .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
                .pNext = nullptr,
                .image = vkInfo.swapchain.displayImages_[i],
                .viewType = VK_IMAGE_VIEW_TYPE_2D,
                .format = vkInfo.swapchain.displayFormat_,
                .components =
                        {
                                .r = VK_COMPONENT_SWIZZLE_R,
                                .g = VK_COMPONENT_SWIZZLE_G,
                                .b = VK_COMPONENT_SWIZZLE_B,
                                .a = VK_COMPONENT_SWIZZLE_A,
                        },
                .subresourceRange =
                        {
                                .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
                                .baseMipLevel = 0,
                                .levelCount = 1,
                                .baseArrayLayer = 0,
                                .layerCount = 1,
                        },
                .flags = 0,
        };
        CALL_VK(vkCreateImageView(vkInfo.device.logicalDevice_, &viewCreateInfo, nullptr,
                                  &vkInfo.swapchain.displayViews_[i]));
    }

    delete[] formats;
}

void CreateDepthBuffer(){

    vkInfo.depth.format_ = VK_FORMAT_D16_UNORM;

    VkImageCreateInfo image_info = {
        .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
        .pNext = NULL,
        .imageType = VK_IMAGE_TYPE_2D,
        .format = vkInfo.depth.format_,
        .extent.width = vkInfo.swapchain.displaySize_.width,
        .extent.height = vkInfo.swapchain.displaySize_.height,
        .extent.depth = 1,
        .mipLevels = 1,
        .arrayLayers = 1,
        .samples = VK_SAMPLE_COUNT_1_BIT,
        .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
        .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
        .queueFamilyIndexCount = 1,
        .pQueueFamilyIndices = &vkInfo.device.queueFamilyIndex_,
        .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
        .flags = 0,
    };
    VkFormatProperties props;
    vkGetPhysicalDeviceFormatProperties(vkInfo.device.gpuDevice_, vkInfo.depth.format_, &props);
    if (props.linearTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) {
        image_info.tiling = VK_IMAGE_TILING_LINEAR;
    } else if (props.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) {
        image_info.tiling = VK_IMAGE_TILING_OPTIMAL;
    } else {
        LOGE("CreateDepthBuffer failed. return.");
        return;
    }

    /* Create image */
    CALL_VK(vkCreateImage(vkInfo.device.logicalDevice_, &image_info, NULL, &vkInfo.depth.image_));

    VkMemoryAllocateInfo mem_alloc = {};
    mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    mem_alloc.pNext = NULL;
    mem_alloc.allocationSize = 0;
    mem_alloc.memoryTypeIndex = 0;



    VkMemoryRequirements mem_reqs;
    vkGetImageMemoryRequirements(vkInfo.device.logicalDevice_, vkInfo.depth.image_, &mem_reqs);

    mem_alloc.allocationSize = mem_reqs.size;
    /* Use the memory properties to determine the type of memory required */
    bool pass = MapMemoryTypeToIndex( mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &mem_alloc.memoryTypeIndex);
    assert(pass);

    /* Allocate memory */
    CALL_VK( vkAllocateMemory(vkInfo.device.logicalDevice_, &mem_alloc, NULL, &vkInfo.depth.memory_) );

    /* Bind memory */
    CALL_VK( vkBindImageMemory(vkInfo.device.logicalDevice_, vkInfo.depth.image_, vkInfo.depth.memory_, 0) );

    /* Create image view */
    VkImageViewCreateInfo view_info = {
        .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
        .pNext = NULL,
        .format = vkInfo.depth.format_,
        .components.r = VK_COMPONENT_SWIZZLE_R,
        .components.g = VK_COMPONENT_SWIZZLE_G,
        .components.b = VK_COMPONENT_SWIZZLE_B,
        .components.a = VK_COMPONENT_SWIZZLE_A,
        .subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
        .subresourceRange.baseMipLevel = 0,
        .subresourceRange.levelCount = 1,
        .subresourceRange.baseArrayLayer = 0,
        .subresourceRange.layerCount = 1,
        .viewType = VK_IMAGE_VIEW_TYPE_2D,
        .flags = 0,
        .image = vkInfo.depth.image_,
    };

    CALL_VK( vkCreateImageView(vkInfo.device.logicalDevice_, &view_info, NULL, &vkInfo.depth.imageView_) );

}

void CreateFrameBuffers() {
    // create a framebuffer from each swapchain image
    vkInfo.swapchain.framebuffers_.resize(vkInfo.swapchain.length_);
    VkImageView attachments[2];
    attachments[1] = vkInfo.depth.imageView_;
    for (uint32_t i = 0; i < vkInfo.swapchain.length_; i++) {
        attachments[0] = vkInfo.swapchain.displayViews_[i];
        VkFramebufferCreateInfo fbCreateInfo{
                .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
                .pNext = nullptr,
                .renderPass = vkInfo.renderPass_,
                .layers = 1,
                .attachmentCount = 2,  // 2 if using depth
                .pAttachments = attachments,
                .width = vkInfo.swapchain.displaySize_.width,
                .height = vkInfo.swapchain.displaySize_.height,
        };

        CALL_VK(vkCreateFramebuffer(vkInfo.device.logicalDevice_, &fbCreateInfo, nullptr,
                                    &vkInfo.swapchain.framebuffers_[i]));
    }
}
bool CreateUniformBuffer(){

    /* VULKAN_KEY_START */
    VkBufferCreateInfo buf_info = {};
    buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
    buf_info.pNext = NULL;
    buf_info.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
    buf_info.size = sizeof(MVPmatrix);
    buf_info.queueFamilyIndexCount = 1;
    buf_info.pQueueFamilyIndices = &vkInfo.device.queueFamilyIndex_;
    buf_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    buf_info.flags = 0;
    CALL_VK( vkCreateBuffer(vkInfo.device.logicalDevice_, &buf_info, NULL, &vkInfo.uniform.buffer_) );

    VkMemoryRequirements mem_reqs;
    vkGetBufferMemoryRequirements(vkInfo.device.logicalDevice_, vkInfo.uniform.buffer_, &mem_reqs);

    VkMemoryAllocateInfo alloc_info = {};
    alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    alloc_info.pNext = NULL;
    alloc_info.memoryTypeIndex = 0;

    alloc_info.allocationSize = mem_reqs.size;
    bool pass = MapMemoryTypeToIndex( mem_reqs.memoryTypeBits,
                                       VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
                                       &alloc_info.memoryTypeIndex);
    assert(pass && "No mappable, coherent memory");

    CALL_VK( vkAllocateMemory(vkInfo.device.logicalDevice_, &alloc_info, NULL, &vkInfo.uniform.memory_) );
    CALL_VK( vkMapMemory(vkInfo.device.logicalDevice_, vkInfo.uniform.memory_, 0, mem_reqs.size, 0, &vkInfo.uniform.pData) );
    CALL_VK( vkBindBufferMemory(vkInfo.device.logicalDevice_, vkInfo.uniform.buffer_, vkInfo.uniform.memory_, 0) );
    return true;
}
// Create our vertex buffer
bool CreateVertexBuffers(void) {

    // Create a vertex buffer
    VkBufferCreateInfo createBufferInfo{
            .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
            .pNext = nullptr,
            .size = sizeof(g_vb_solid_face_colors_Data),
            .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
            .flags = 0,
            .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
            .pQueueFamilyIndices = &vkInfo.device.queueFamilyIndex_,
            .queueFamilyIndexCount = 1,
    };

    CALL_VK(vkCreateBuffer(vkInfo.device.logicalDevice_, &createBufferInfo, nullptr,
                           &vkInfo.vertex.buffer_));

    VkMemoryRequirements memReq;
    vkGetBufferMemoryRequirements(vkInfo.device.logicalDevice_, vkInfo.vertex.buffer_, &memReq);

    VkMemoryAllocateInfo allocInfo{
            .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
            .pNext = nullptr,
            .allocationSize = memReq.size,
            .memoryTypeIndex = 0,  // Memory type assigned in the next step
    };

    // Assign the proper memory type for that buffer
    MapMemoryTypeToIndex(memReq.memoryTypeBits,
                         VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
                         VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
                         &allocInfo.memoryTypeIndex);

    // Allocate memory for the buffer
    CALL_VK(vkAllocateMemory(vkInfo.device.logicalDevice_, &allocInfo, nullptr, &vkInfo.vertex.memory_));

    CALL_VK(vkMapMemory(vkInfo.device.logicalDevice_, vkInfo.vertex.memory_, 0, allocInfo.allocationSize,
                        0, &vkInfo.vertex.pData));
    memcpy(vkInfo.vertex.pData, g_vb_solid_face_colors_Data, sizeof(g_vb_solid_face_colors_Data));
    vkUnmapMemory(vkInfo.device.logicalDevice_, vkInfo.vertex.memory_);

    CALL_VK( vkBindBufferMemory(vkInfo.device.logicalDevice_, vkInfo.vertex.buffer_, vkInfo.vertex.memory_, 0));
    return true;
}


enum ShaderType {
    VERTEX_SHADER, FRAGMENT_SHADER
};

VkResult loadShaderFromFile(const char *filePath, VkShaderModule *shaderOut,
                            ShaderType type) {
    // Read the file
    assert(androidAppCtx);
    AAsset *file = AAssetManager_open(androidAppCtx->activity->assetManager,
                                      filePath, AASSET_MODE_BUFFER);
    size_t fileLength = AAsset_getLength(file);

    char *fileContent = new char[fileLength];

    AAsset_read(file, fileContent, fileLength);
    AAsset_close(file);

    VkShaderModuleCreateInfo shaderModuleCreateInfo{
            .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
            .pNext = nullptr,
            .codeSize = fileLength,
            .pCode = (const uint32_t *) fileContent,
            .flags = 0,
    };
    VkResult result = vkCreateShaderModule(
            vkInfo.device.logicalDevice_, &shaderModuleCreateInfo, nullptr, shaderOut);
    assert(result == VK_SUCCESS);

    delete[] fileContent;

    return result;
}

void CreatePipeLineLayout()
{
    VkDescriptorSetLayoutBinding layout_binding = {};
    layout_binding.binding = 0;
    layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
    layout_binding.descriptorCount = 1;
    layout_binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
    layout_binding.pImmutableSamplers = NULL;

    /* Next take layout bindings and use them to create a descriptor set layout
     */
    VkDescriptorSetLayoutCreateInfo layout_info = {};
    layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
    layout_info.pNext = NULL;
    layout_info.bindingCount = 1;
    layout_info.pBindings = &layout_binding;

    CALL_VK( vkCreateDescriptorSetLayout(vkInfo.device.logicalDevice_, &layout_info, NULL, &vkInfo.descriptor.layout_));

    /* Now use the descriptor layout to create a pipeline layout */
    VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = {};
    pipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
    pipelineLayoutCreateInfo.pNext = NULL;
    pipelineLayoutCreateInfo.pushConstantRangeCount = 0;
    pipelineLayoutCreateInfo.pPushConstantRanges = NULL;
    pipelineLayoutCreateInfo.setLayoutCount = 1;
    pipelineLayoutCreateInfo.pSetLayouts = &vkInfo.descriptor.layout_;

    CALL_VK(vkCreatePipelineLayout(vkInfo.device.logicalDevice_, &pipelineLayoutCreateInfo,
                                   nullptr, &vkInfo.pipeline.layout_));
}

void CreateDescriptorSet()
{


    VkDescriptorPoolSize type_count[1];
    type_count[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
    type_count[0].descriptorCount = 1;

    VkDescriptorPoolCreateInfo descriptor_pool = {};
    descriptor_pool.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
    descriptor_pool.pNext = NULL;
    descriptor_pool.maxSets = 1;
    descriptor_pool.poolSizeCount = 1;
    descriptor_pool.pPoolSizes = type_count;

    CALL_VK( vkCreateDescriptorPool(vkInfo.device.logicalDevice_, &descriptor_pool, NULL, &vkInfo.descriptor.pool_) );

    VkDescriptorSetAllocateInfo alloc_info[1];
    alloc_info[0].sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
    alloc_info[0].pNext = NULL;
    alloc_info[0].descriptorPool = vkInfo.descriptor.pool_;
    alloc_info[0].descriptorSetCount = 1;
    alloc_info[0].pSetLayouts = &vkInfo.descriptor.layout_;

    CALL_VK(vkAllocateDescriptorSets(vkInfo.device.logicalDevice_, alloc_info, &vkInfo.descriptor.set_));

    VkWriteDescriptorSet writes[1];
    writes[0] = {};
    writes[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
    writes[0].pNext = NULL;
    writes[0].dstSet = vkInfo.descriptor.set_;
    writes[0].descriptorCount = 1;
    writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
    writes[0].dstArrayElement = 0;
    writes[0].dstBinding = 0;

    VkDescriptorBufferInfo info;
    info.buffer = vkInfo.uniform.buffer_;
    info.offset = 0;
    info.range = sizeof(MVPmatrix);
    writes[0].pBufferInfo = &info;

    vkUpdateDescriptorSets(vkInfo.device.logicalDevice_, 1, writes, 0, NULL);
}

void CreateRenderPass(){
    // We need to create a semaphore to be able to wait, in the main loop, for our
    // framebuffer to be available for us before drawing.
    VkSemaphoreCreateInfo semaphoreCreateInfo{
            .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
            .pNext = nullptr,
            .flags = 0,
    };
    CALL_VK( vkCreateSemaphore(vkInfo.device.logicalDevice_, &semaphoreCreateInfo, nullptr, &vkInfo.semaphore_));

    VkFenceCreateInfo fenceInfo;
    fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
    fenceInfo.pNext = NULL;
    fenceInfo.flags = 0;
    CALL_VK( vkCreateFence(vkInfo.device.logicalDevice_, &fenceInfo, NULL, &vkInfo.fence_) );


    VkAttachmentDescription attachment[2];
    attachment[0].format = vkInfo.swapchain.displayFormat_;
    attachment[0].samples = VK_SAMPLE_COUNT_1_BIT;
    attachment[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
    attachment[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
    attachment[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    attachment[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    attachment[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    attachment[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;

    attachment[0].format = vkInfo.depth.format_;
    attachment[0].samples = VK_SAMPLE_COUNT_1_BIT;
    attachment[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
    attachment[0].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    attachment[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    attachment[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
    attachment[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    attachment[0].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

    VkAttachmentReference color_reference = {};
    color_reference.attachment = 0;
    color_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

    VkAttachmentReference depth_reference = {};
    depth_reference.attachment = 1;
    depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

    VkSubpassDescription subpassDescription{
            .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
            .flags = 0,
            .inputAttachmentCount = 0,
            .pInputAttachments = nullptr,
            .colorAttachmentCount = 1,
            .pColorAttachments = &color_reference,
            .pResolveAttachments = nullptr,
            .pDepthStencilAttachment = &depth_reference,
            .preserveAttachmentCount = 0,
            .pPreserveAttachments = nullptr,
    };
    // Subpass dependency to wait for wsi image acquired semaphore before starting layout transition
    VkSubpassDependency subpass_dependency = {
             .srcSubpass = VK_SUBPASS_EXTERNAL,
             .dstSubpass = 0,
             .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
             .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
             .srcAccessMask = 0,
             .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
             .dependencyFlags = 0,
    };

    VkRenderPassCreateInfo renderPassCreateInfo{
            .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
            .pNext = nullptr,
            .attachmentCount = 2,
            .pAttachments = attachment,
            .subpassCount = 1,
            .pSubpasses = &subpassDescription,
            .dependencyCount = 0,
            .pDependencies = &subpass_dependency,
    };


    CALL_VK(vkCreateRenderPass(vkInfo.device.logicalDevice_, &renderPassCreateInfo, nullptr,
                               &vkInfo.renderPass_));

}

void CreateShader(){
    loadShaderFromFile("shaders/tri.vert.spv", &vkInfo.vertexShader_, VERTEX_SHADER);
    loadShaderFromFile("shaders/tri.frag.spv", &vkInfo.fragmentShader_, FRAGMENT_SHADER);

    // Specify vertex and fragment shader stages
    shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
    shaderStages[0].pNext = nullptr;
    shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
    shaderStages[0].module = vkInfo.vertexShader_;
    shaderStages[0].pSpecializationInfo = nullptr;
    shaderStages[0].flags = 0;
    shaderStages[0].pName = "main";

    shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
    shaderStages[1].pNext = nullptr;
    shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
    shaderStages[1].module = vkInfo.fragmentShader_;
    shaderStages[1].pSpecializationInfo = nullptr;
    shaderStages[1].flags = 0;
    shaderStages[1].pName = "main";

}

// Create Graphics Pipeline
void CreatePipeline(void) {
    VkDynamicState dynamicStateEnables[VK_DYNAMIC_STATE_RANGE_SIZE];
    VkPipelineDynamicStateCreateInfo dynamicState = {};
    memset(dynamicStateEnables, 0, sizeof dynamicStateEnables);
    dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
    dynamicState.pNext = NULL;
    dynamicState.pDynamicStates = dynamicStateEnables;
    dynamicState.dynamicStateCount = 0;

    VkVertexInputBindingDescription vertex_input_bindings{
            .binding = 0,
            .stride = 8 * sizeof(float),
            .inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
    };
    VkVertexInputAttributeDescription vertex_input_attributes[2] = {
        {
                .binding = 0,
                .location = 0,
                .format = VK_FORMAT_R32G32B32A32_SFLOAT,
                .offset = 0,
        },
        {
                .binding = 0,
                .location = 1,
                .format = VK_FORMAT_R32G32B32A32_SFLOAT,
                .offset = 4 * sizeof(float),
        },
    };
    VkPipelineVertexInputStateCreateInfo vi;
    vi.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
    vi.pNext = NULL;
    vi.flags = 0;
    vi.vertexBindingDescriptionCount = 1;
    vi.pVertexBindingDescriptions = &vertex_input_bindings;
    vi.vertexAttributeDescriptionCount = 2;
    vi.pVertexAttributeDescriptions = vertex_input_attributes;

    VkPipelineInputAssemblyStateCreateInfo ia;
    ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
    ia.pNext = NULL;
    ia.flags = 0;
    ia.primitiveRestartEnable = VK_FALSE;
    ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;

    VkPipelineRasterizationStateCreateInfo rs;
    rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
    rs.pNext = NULL;
    rs.flags = 0;
    rs.polygonMode = VK_POLYGON_MODE_FILL;
    rs.cullMode = VK_CULL_MODE_FRONT_BIT;
    rs.frontFace = VK_FRONT_FACE_CLOCKWISE;
    rs.depthClampEnable = VK_FALSE;
    rs.rasterizerDiscardEnable = VK_FALSE;
    rs.depthBiasEnable = VK_FALSE;
    rs.depthBiasConstantFactor = 0;
    rs.depthBiasClamp = 0;
    rs.depthBiasSlopeFactor = 0;
    rs.lineWidth = 1.0f;

    VkPipelineColorBlendStateCreateInfo cb;
    cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
    cb.pNext = NULL;
    cb.flags = 0;
    VkPipelineColorBlendAttachmentState att_state[1];
    att_state[0].colorWriteMask = 0xf;
    att_state[0].blendEnable = VK_FALSE;
    att_state[0].alphaBlendOp = VK_BLEND_OP_ADD;
    att_state[0].colorBlendOp = VK_BLEND_OP_ADD;
    att_state[0].srcColorBlendFactor = VK_BLEND_FACTOR_ZERO;
    att_state[0].dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
    att_state[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
    att_state[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
    cb.attachmentCount = 1;
    cb.pAttachments = att_state;
    cb.logicOpEnable = VK_FALSE;
    cb.logicOp = VK_LOGIC_OP_NO_OP;
    cb.blendConstants[0] = 1.0f;
    cb.blendConstants[1] = 1.0f;
    cb.blendConstants[2] = 1.0f;
    cb.blendConstants[3] = 1.0f;

    vkInfo.viewport = {
            .width = static_cast<float>(vkInfo.swapchain.displaySize_.width),
            .height = static_cast<float>(vkInfo.swapchain.displaySize_.height),
            .minDepth = 0.0f,
            .maxDepth = 1.0f,
            .x = 0,
            .y = 0
    };
    vkInfo.scissor = {
            .extent.width = vkInfo.swapchain.displaySize_.width,
            .extent.height = vkInfo.swapchain.displaySize_.height,
            .offset.x = 0,
            .offset.y = 0
    };

    VkPipelineViewportStateCreateInfo vp = {};
    vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
    vp.pNext = NULL;
    vp.flags = 0;
    vp.viewportCount = 1;
    dynamicStateEnables[dynamicState.dynamicStateCount++] = VK_DYNAMIC_STATE_VIEWPORT;
    vp.scissorCount = 1;
    dynamicStateEnables[dynamicState.dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR;
    vp.pScissors = &vkInfo.scissor;
    vp.pViewports = &vkInfo.viewport;

    VkPipelineDepthStencilStateCreateInfo ds;
    ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
    ds.pNext = NULL;
    ds.flags = 0;
    ds.depthTestEnable = VK_TRUE;
    ds.depthWriteEnable = VK_TRUE;
    ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
    ds.depthBoundsTestEnable = VK_FALSE;
    ds.minDepthBounds = 0;
    ds.maxDepthBounds = 0;
    ds.stencilTestEnable = VK_FALSE;
    ds.back.failOp = VK_STENCIL_OP_KEEP;
    ds.back.passOp = VK_STENCIL_OP_KEEP;
    ds.back.compareOp = VK_COMPARE_OP_ALWAYS;
    ds.back.compareMask = 0;
    ds.back.reference = 0;
    ds.back.depthFailOp = VK_STENCIL_OP_KEEP;
    ds.back.writeMask = 0;
    ds.front = ds.back;

    VkPipelineMultisampleStateCreateInfo ms;
    ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
    ms.pNext = NULL;
    ms.flags = 0;
    ms.pSampleMask = NULL;
    ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
    ms.sampleShadingEnable = VK_FALSE;
    ms.alphaToCoverageEnable = VK_FALSE;
    ms.alphaToOneEnable = VK_FALSE;
    ms.minSampleShading = 0.0;

    VkGraphicsPipelineCreateInfo pipeline;
    pipeline.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
    pipeline.pNext = NULL;
    pipeline.layout = vkInfo.pipeline.layout_;
    pipeline.basePipelineHandle = VK_NULL_HANDLE;
    pipeline.basePipelineIndex = 0;
    pipeline.flags = 0;
    pipeline.pVertexInputState = &vi;
    pipeline.pInputAssemblyState = &ia;
    pipeline.pRasterizationState = &rs;
    pipeline.pColorBlendState = &cb;
    pipeline.pTessellationState = NULL;
    pipeline.pMultisampleState = &ms;
    pipeline.pDynamicState = &dynamicState;
    pipeline.pViewportState = &vp;
    pipeline.pDepthStencilState = &ds;
    pipeline.pStages = shaderStages;
    pipeline.stageCount = 2;
    pipeline.renderPass = vkInfo.renderPass_;
    pipeline.subpass = 0;

    CALL_VK( vkCreateGraphicsPipelines(vkInfo.device.logicalDevice_, VK_NULL_HANDLE, 1, &pipeline, NULL, &vkInfo.pipeline.pipeline_) );
}

glm::mat4 model = glm::translate( glm::mat4(1.0f), glm::vec3(0,0,10));
glm::mat4 view = glm::lookAt(glm::vec3(0, 0, 0),
                             glm::vec3(0, 0, 10),
                             glm::vec3(0, 1, 0)
);
float fov = glm::radians(90.0f);
float aspect = 1.0f;
glm::mat4 project = glm::perspective(fov, aspect, 0.1f, 100.0f);

void UpdateMVP(){
    model = glm::rotate(model,glm::radians(2.0f),glm::vec3(1,1,1));
    aspect = (float)vkInfo.swapchain.displaySize_.width / (float)vkInfo.swapchain.displaySize_.height;
    project = glm::perspective(fov, aspect, 0.1f, 100.0f);
    glm::mat4 MVP =  project * view * model;

    memcpy(MVPmatrix, glm::value_ptr(MVP), sizeof(MVPmatrix));
    memcpy(vkInfo.uniform.pData, MVPmatrix, sizeof(MVPmatrix));

}
void RecordCommands(uint32_t bufferIndex){
        // We start by creating and declare the "beginning" our command buffer
        VkCommandBufferBeginInfo cmdBufferBeginInfo{
                .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
                .pNext = nullptr,
                .flags = 0,
                .pInheritanceInfo = nullptr,
        };
        CALL_VK(vkBeginCommandBuffer(vkInfo.command.buffer_[bufferIndex], &cmdBufferBeginInfo));


        // Now we start a renderpass. Any draw command has to be recorded in a
        // renderpass
        VkClearValue clear_values[2];
        clear_values[0].color.float32[0] = 0.5f;
        clear_values[0].color.float32[1] = 0.5f;
        clear_values[0].color.float32[2] = 0.0f;
        clear_values[0].color.float32[3] = 1.0f;
        clear_values[1].depthStencil.depth = 1.0f;
        clear_values[1].depthStencil.stencil = 0;

        VkRenderPassBeginInfo renderPassBeginInfo{
                .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
                .pNext = nullptr,
                .renderPass = vkInfo.renderPass_,
                .framebuffer = vkInfo.swapchain.framebuffers_[bufferIndex],
                .renderArea.offset.x = 0,
                .renderArea.offset.y = 0,
                .renderArea.extent.width = vkInfo.swapchain.displaySize_.width,
                .renderArea.extent.height = vkInfo.swapchain.displaySize_.height,
                .clearValueCount = 2,
                .pClearValues = clear_values};
        vkCmdBeginRenderPass(vkInfo.command.buffer_[bufferIndex], &renderPassBeginInfo,
                             VK_SUBPASS_CONTENTS_INLINE);
        // Bind what is necessary to the command buffer
        vkCmdBindPipeline(vkInfo.command.buffer_[bufferIndex],
                          VK_PIPELINE_BIND_POINT_GRAPHICS, vkInfo.pipeline.pipeline_);
        vkCmdBindDescriptorSets(
                vkInfo.command.buffer_[bufferIndex],
                VK_PIPELINE_BIND_POINT_GRAPHICS,
                vkInfo.pipeline.layout_,
                0,
                1,
                &vkInfo.descriptor.set_,
                0,
                NULL);
        VkDeviceSize offset = 0;
        vkCmdBindVertexBuffers(vkInfo.command.buffer_[bufferIndex], 0, 1,
                               &vkInfo.vertex.buffer_, &offset);

        vkCmdSetViewport(vkInfo.command.buffer_[bufferIndex],0,1,&vkInfo.viewport);
        vkCmdSetScissor(vkInfo.command.buffer_[bufferIndex],0,1,&vkInfo.scissor);
        // Draw Triangle
        vkCmdDraw(vkInfo.command.buffer_[bufferIndex], 36, 1, 0, 0);

        vkCmdEndRenderPass(vkInfo.command.buffer_[bufferIndex]);

        CALL_VK(vkEndCommandBuffer(vkInfo.command.buffer_[bufferIndex]));
}


//   Initialize Vulkan Context when android application window is created
//   upon return, vulkan is ready to draw frames
bool InitializeVulkan(android_app *app) {
    androidAppCtx = app;

    if (!InitVulkan()) {
        LOGW("Vulkan is unavailable, install vulkan and re-start");
        return false;
    }
    CreateVulkanInstance(app->window);
    CreateVulkanDevice();
    CreateSwapChain(app->window);
    CreateCommandBuffer();
    CreateDepthBuffer();
    CreateUniformBuffer();
    CreatePipeLineLayout();
    CreateDescriptorSet();
    CreateRenderPass();
    CreateShader();
    CreateFrameBuffers();
    CreateVertexBuffers();  // create vertex buffers
    CreatePipeline();
    RecordCommands(0);
    RecordCommands(1);

    vkInfo.initialized_ = true;
    return vkInfo.initialized_ ;
}

//    native app poll to see if we are ready to draw...
bool IsVulkanReady(void) { return vkInfo.initialized_; }

void DeleteVulkan(void) {
    //free pipeline
    vkDestroyPipeline(vkInfo.device.logicalDevice_, vkInfo.pipeline.pipeline_, nullptr);
    //free vertex buffer
    vkDestroyBuffer(vkInfo.device.logicalDevice_,vkInfo.vertex.buffer_, nullptr);
    vkFreeMemory(vkInfo.device.logicalDevice_,vkInfo.vertex.memory_, nullptr);
    //free framebuffer
    for (uint32_t i = 0; i < vkInfo.swapchain.length_; i++) {
        vkDestroyFramebuffer(vkInfo.device.logicalDevice_,vkInfo.swapchain.framebuffers_[i], nullptr);
    }
    //free shader module
    vkDestroyShaderModule(vkInfo.device.logicalDevice_, vkInfo.vertexShader_, nullptr);
    vkDestroyShaderModule(vkInfo.device.logicalDevice_, vkInfo.fragmentShader_, nullptr);
    //free render pass
    vkDestroyFence(vkInfo.device.logicalDevice_,vkInfo.fence_, nullptr);
    vkDestroySemaphore(vkInfo.device.logicalDevice_,vkInfo.semaphore_, nullptr);
    vkDestroyRenderPass(vkInfo.device.logicalDevice_, vkInfo.renderPass_, nullptr);
    //free pipeline layout
    vkDestroyPipelineLayout(vkInfo.device.logicalDevice_, vkInfo.pipeline.layout_, nullptr);
    //free descriptor
    vkDestroyDescriptorSetLayout(vkInfo.device.logicalDevice_,vkInfo.descriptor.layout_,nullptr);
    vkDestroyDescriptorPool(vkInfo.device.logicalDevice_,vkInfo.descriptor.pool_,nullptr);
    //free uniform buffer
    vkUnmapMemory(vkInfo.device.logicalDevice_,vkInfo.uniform.memory_);
    vkDestroyBuffer(vkInfo.device.logicalDevice_,vkInfo.uniform.buffer_, nullptr);
    vkFreeMemory(vkInfo.device.logicalDevice_,vkInfo.uniform.memory_, nullptr);
    //free depth buffer
    vkDestroyImageView(vkInfo.device.logicalDevice_, vkInfo.depth.imageView_, nullptr);
    vkDestroyImage(vkInfo.device.logicalDevice_, vkInfo.depth.image_, nullptr);
    vkFreeMemory(vkInfo.device.logicalDevice_, vkInfo.depth.memory_, nullptr);
    //free swapchain
    for (int i = 0; i < vkInfo.swapchain.length_; i++) {
        vkDestroyFramebuffer(vkInfo.device.logicalDevice_, vkInfo.swapchain.framebuffers_[i], nullptr);
        vkDestroyImageView(vkInfo.device.logicalDevice_, vkInfo.swapchain.displayViews_[i], nullptr);
        vkDestroyImage(vkInfo.device.logicalDevice_, vkInfo.swapchain.displayImages_[i], nullptr);
    }
    vkDestroySwapchainKHR(vkInfo.device.logicalDevice_, vkInfo.swapchain.swapchain_, nullptr);
    //free command buffer
    vkFreeCommandBuffers(vkInfo.device.logicalDevice_, vkInfo.command.pool_, vkInfo.command.length_, vkInfo.command.buffer_);
    vkDestroyCommandPool(vkInfo.device.logicalDevice_, vkInfo.command.pool_, nullptr);

    vkDestroyDevice(vkInfo.device.logicalDevice_, nullptr);
    vkDestroyInstance(vkInfo.instance_, nullptr);

    vkInfo.initialized_ = false;
}

// Draw one frame
bool VulkanDrawFrame(void) {
    UpdateMVP();

    uint32_t nextIndex;
    // Get the framebuffer index we should draw in
    CALL_VK(vkAcquireNextImageKHR(vkInfo.device.logicalDevice_, vkInfo.swapchain.swapchain_,
                                  UINT64_MAX, vkInfo.semaphore_, VK_NULL_HANDLE,
                                  &nextIndex));
    CALL_VK(vkResetFences(vkInfo.device.logicalDevice_, 1, &vkInfo.fence_));


    VkPipelineStageFlags waitStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    VkSubmitInfo submit_info = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
            .pNext = nullptr,
            .waitSemaphoreCount = 1,
            .pWaitSemaphores = &vkInfo.semaphore_,
            .pWaitDstStageMask = &waitStageMask,
            .commandBufferCount = 1,
            .pCommandBuffers = &vkInfo.command.buffer_[nextIndex],
            .signalSemaphoreCount = 0,
            .pSignalSemaphores = nullptr};
    CALL_VK(vkQueueSubmit(vkInfo.device.queue_, 1, &submit_info, vkInfo.fence_));
    CALL_VK(vkWaitForFences(vkInfo.device.logicalDevice_, 1, &vkInfo.fence_, VK_TRUE, 100000000));


    VkResult result;
    VkPresentInfoKHR presentInfo{
            .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
            .pNext = nullptr,
            .swapchainCount = 1,
            .pSwapchains = &vkInfo.swapchain.swapchain_,
            .pImageIndices = &nextIndex,
            .waitSemaphoreCount = 0,
            .pWaitSemaphores = nullptr,
            .pResults = &result,
    };
    vkQueuePresentKHR(vkInfo.device.queue_, &presentInfo);
    LOGI("Drawing frames index %d...... present result %d",nextIndex,result);
    return true;
}
